//-------------------------------------------------------------------
//	Copyright (c) 2013-2014  Zhirnov Andrey
//	This file is part of the "UX OpwnGL Framework" project.
//	See copyright notice in "UX_GL_Framework.h".
//-------------------------------------------------------------------

#if 1

#include <Common.h>
#include <noise.h>

#pragma comment (lib, "libnoise.lib")

typedef pod_array< real >	real_array_t;

class TestModule
{
public:
	double GetValue (double x, double y, double z)
	{
		return x + y * 1.0e3 + z * 1.0e6;
	}
};

bool RunCLKernel (CLQueue &clQueue, const CLProgramPtr &prog, CLImage &image,
				  uni_c_string kernelName, real_array_t &pixels, const uvec2 &dim)
{
	CLKernel	kernel;
	kernel.Create( prog.ptr(), kernelName.cstr() );
	kernel.SetArg( 0, image.Id() );

	CHECK_RETURN( clQueue.EnqueueNDRange( &kernel, svec3(0), svec3(dim.x, dim.y, 0), svec3(0) ) );
	CHECK_RETURN( image.EnqueueRead( &clQueue, false, svec3(0), svec3(dim, 1), svec2(0), pixels.ptr() ) );
	CHECK_RETURN( clQueue.Finish() );
	return true;
}


template <typename T>
bool RunLibNoise (real_array_t &pixels, const uvec2 &dim)
{
	T	module;

	for (uint y = 0; y < dim.y; ++y)
	{
		for (uint x = 0; x < dim.x; ++x)
		{
			double	dx = double(x) / dim.x;
			double	dy = double(y) / dim.y;
			double	dz = 0.0;
			usize	i  = x + y * dim.x;

			pixels[i] = (real) module.GetValue( dx, dy, dz );
		}
	}

	return true;
}


bool Compare (const real_array_t &left, const real_array_t &right)
{
	FOR( i, left )
	{
		const real l_value = left[i];
		const real r_value = right[i];

		if ( not Equals( l_value, r_value, 16 ) )
			return false;
	}
	return true;
}


template <typename T>
bool RunTest (CLQueue &clQueue, const CLProgramPtr &prog, CLImage &image,
			  uni_c_string kernelName, real_array_t &leftPixels, real_array_t &rightPixels, const uvec2 &dim)
{
	CHECK_RETURN( RunCLKernel( clQueue, prog, image, kernelName, leftPixels, dim ) );
	CHECK_RETURN( RunLibNoise< T >( rightPixels, dim ) );
	CHECK_RETURN( Compare( leftPixels, rightPixels ) );
	return true;
}


bool Test_libNoise ()
{
	// program //
	CLProgramPtr	prog;
	ShaderLoader::Load( prog, "../Tests/data/libNoise.cl" );
	

	// data //
	const uvec2		dim( 8 );
	real_array_t	cl_pixels;		cl_pixels.Resize( dim.Square(), false );
	real_array_t	ln_pixels;		ln_pixels.Resize( dim.Square(), false );


	// queue //
	CLQueue			cl_queue;
	CHECK_RETURN( cl_queue.Create( cl_queue_property::NONE ) );


	// image //
	CLImage			image;
	cl_memory::type	flags = cl_memory::WRITE_ONLY;

	cl_img_format	fmt;

	if ( sizeof(real) == sizeof(float) )
	{
		fmt.eOrder	 = cl_img_channel_order::R;
		fmt.eChannel = cl_img_channel::FLOAT;
	}
	if ( sizeof(real) == sizeof(double) )
	{
		fmt.eOrder	 = cl_img_channel_order::RG;
		fmt.eChannel = cl_img_channel::SINT32;
	}
	CHECK_RETURN( image.Create2D( dim.xy(), flags, fmt ) );
	

	// tests //
	CHECK_RETURN( RunTest< TestModule >( cl_queue, prog, image, "TestEntry", cl_pixels, ln_pixels, dim ) );
	CHECK_RETURN( RunTest< noise::module::Billow >( cl_queue, prog, image, "BillowEntry", cl_pixels, ln_pixels, dim ) );
	CHECK_RETURN( RunTest< noise::module::Checkerboard >( cl_queue, prog, image, "CheckerboardEntry", cl_pixels, ln_pixels, dim ) );
	//CHECK_RETURN( RunTest< noise::module::Curve >( cl_queue, prog, image, "CurveEntry", cl_pixels, ln_pixels, dim ) );
	CHECK_RETURN( RunTest< noise::module::Cylinders >( cl_queue, prog, image, "CylindersEntry", cl_pixels, ln_pixels, dim ) );
	CHECK_RETURN( RunTest< noise::module::Perlin >( cl_queue, prog, image, "PerlinEntry", cl_pixels, ln_pixels, dim ) );
	CHECK_RETURN( RunTest< noise::module::RidgedMulti >( cl_queue, prog, image, "RidgedMultiEntry", cl_pixels, ln_pixels, dim ) );
	//CHECK_RETURN( RunTest< noise::module::Select >( cl_queue, prog, image, "SelectEntry", cl_pixels, ln_pixels, dim ) );
	CHECK_RETURN( RunTest< noise::module::Spheres >( cl_queue, prog, image, "SpheresEntry", cl_pixels, ln_pixels, dim ) );
	//CHECK_RETURN( RunTest< noise::module::Terrace >( cl_queue, prog, image, "TerraceEntry", cl_pixels, ln_pixels, dim ) );
	//CHECK_RETURN( RunTest< noise::module::Turbulence >( cl_queue, prog, image, "TurbulenceEntry", cl_pixels, ln_pixels, dim ) );
	CHECK_RETURN( RunTest< noise::module::Voronoi >( cl_queue, prog, image, "VoronoiEntry", cl_pixels, ln_pixels, dim ) );

	return true;
}

#else

bool Test_libNoise ()
{
	return true;
}

#endif